﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SimpleGraphLib
{
    public class Graph<TVertex> : IGraph<TVertex>
    {
        IList<IEdge<TVertex>> _Edges;
        IList<TVertex> _Vertices;
        GraphType _Type;

        public IEnumerable<IEdge<TVertex>> Edges
        {
            get { return _Edges; }
        }

        public IEnumerable<TVertex> Vertices
        {
            get { return Vertices; }
        }

        public GraphType Type
        {
            get { return _Type; }
        }

        #region [Constructors]
        /// <summary>
        /// Create graph of specific type
        /// </summary>
        /// <param name="type"></param>
        public Graph(GraphType type)
        {
            _Edges = new List<IEdge<TVertex>>();
            _Vertices = new List<TVertex>();
            _Type = type;
        }
        #endregion

        /// <summary>
        /// Is edge exists between source and destination
        /// </summary>
        /// <param name="source"></param>
        /// <param name="dest"></param>
        /// <returns></returns>
        public bool IsEdge(TVertex source, TVertex dest)
        {
            return _Edges.Contains(new Edge<TVertex>(source, dest));
        }

        /// <summary>
        /// Add edge between source and destination
        /// </summary>
        /// <param name="source"></param>
        /// <param name="dest"></param>
        /// <param name="weight">optional</param>
        public void AddEdge(TVertex source, TVertex dest, double weight = 0)
        {
            if (!IsEdge(source, dest))
            {
                _Edges.Add(new Edge<TVertex>(source, dest, weight));

                if (_Type == GraphType.Undirected)
                    _Edges.Add(new Edge<TVertex>(dest, source, weight));
            }
        }

        /// <summary>
        /// Remove edge if exists.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="dest"></param>
        public void RemoveEdge(TVertex source, TVertex dest)
        {
            var edge = GetEdge(source, dest);

            if (edge != null)
                _Edges.Remove(edge);
        }

        /// <summary>
        /// Search for edge between source and destination.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="dest"></param>
        /// <returns>IEdge or null</returns>
        public IEdge<TVertex> GetEdge(TVertex source, TVertex dest)
        {
            return _Edges.Where(e => e.Source.Equals(source) && e.Dest.Equals(dest)).SingleOrDefault(); 
        }

        /// <summary>
        /// Add new vertex.
        /// </summary>
        /// <param name="vertex"></param>
        public void AddVertex(TVertex vertex)
        {
            if(!_Vertices.Contains(vertex))
                _Vertices.Add(vertex);
        }

        /// <summary>
        /// Remove the vertex. Removing the vertex removes also all the edges.
        /// </summary>
        /// <param name="vertex"></param>
        /// <returns>true if vertex existed and was removed</returns>
        public bool RemoveVertex(TVertex vertex)
        {
            var result = false;

            if (_Vertices.Contains(vertex))
            {                
                _Vertices.Remove(vertex);

                var edges = _Edges.Where(e => e.Source.Equals(vertex) || e.Dest.Equals(vertex)).ToList();

                if (edges.Count > 0)
                {
                    foreach (var edge in edges)
                    {
                        _Edges.Remove(edge);
                    }
                }

                result = true;
            }

            return result;
        }

        /// <summary>
        /// Is graph complete.
        /// A complete graph is a simple undirected graph in which every pair of distinct vertices is connected by a unique edge.
        /// </summary>
        /// <returns>true if is complete</returns>
        public bool IsComplete()
        {
            throw new NotImplementedException();
        }

    }
}
